package org.btik.platformioplus.ini.completion;

import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.editor.Editor;
import org.btik.platformioplus.icon.PlatformIoPlusIcon;
import org.btik.platformioplus.ini.completion.entity.PioIniItemBuilder;
import org.btik.platformioplus.ini.completion.entity.PlatformRule;
import org.btik.platformioplus.ini.completion.filter.IniTipFilters;
import org.btik.platformioplus.util.DomUtil;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.swing.*;
import java.util.*;
import java.util.regex.Pattern;

import static org.btik.platformioplus.ini.completion.IniMetaXmlConstant.*;
import static org.btik.platformioplus.util.DomUtil.*;
import static org.btik.platformioplus.util.Note.*;
import static org.btik.platformioplus.util.Note.getMsg;

/**
 * platformio.ini提示key
 *
 * @author lustre
 * @since 2022/12/16 17:07
 */
public class PlatformioIniMetaFactory {
    private PlatformioIniMetaFactory() {
    }

    public static PlatformioIniMetaFactory INSTANCE = new PlatformioIniMetaFactory();
    private final HashMap<String, Set<PioIniItemBuilder>> keys = new HashMap<>();

    private final Set<LookupElementBuilder> sections = new HashSet<>();

    private final HashMap<String, Set<PioIniItemBuilder>> values = new HashMap<>();

    private final ArrayList<PlatformRule> platformRules = new ArrayList<>();

    private final byte[] lock = new byte[0];
    private volatile boolean loaded = false;

    static class Arg {
        String text;

        public Arg(String text) {
            this.text = text;
        }

        @Override
        public String toString() {
            return text;
        }
    }

    public void load() {
        synchronized (lock) {
            if (loaded) {
                return;
            }
            loaded = true;
        }
        Icon icon = PlatformIoPlusIcon.PIOPLUS_13;
        Element documentElement;
        try {
            Document treeConf = DomUtil.parse(PlatformioIniMetaFactory.class.getResourceAsStream("/pioplus/platformioIniMeta.xml"));
            documentElement = treeConf.getDocumentElement();

        } catch (Exception e) {
            NOTIFICATION_GROUP.createNotification(getMsg("notification.group.platformio-plus"),
                    getMsgF("platformio.ini.meta.load.failed", e.getMessage()), NotificationType.ERROR).notify(null);
            return;
        }
        eachByTagName(documentElement, SECTION, (section) -> {
            String sectionName = section.getAttribute(NAME);
            // 创建section名称的提示
            sections.add(LookupElementBuilder.create(sectionName).withIcon(icon).bold());
            String desc = section.getAttribute(DESC);
            String sectionKey = '[' + sectionName + ']';
            eachByTagName(section, KEY, (key) -> {
                String keyName = key.getAttribute(NAME);
                String platform = key.getAttribute(PLATFORM);
                String framework = key.getAttribute(FRAMEWORK);
                keys.computeIfAbsent(sectionKey, (sectionName_) -> new HashSet<>())
                        // 创建 key的提示 并补全等号  "key ="
                        .add(new PioIniItemBuilder(LookupElementBuilder
                                .create(keyName).withInsertHandler(PlatformioIniMetaFactory::fixKeyTipSuffix)
                                .withTypeText(desc, true)
                                .withIcon(icon)
                                .bold(), IniTipFilters.getFilter(platform, framework)));

                eachByTagName(key, VALUE, (value) -> {
                    String valueName = value.getTextContent().trim();
                    String vplatform = value.getAttribute(PLATFORM);
                    String vframework = value.getAttribute(FRAMEWORK);
                    values.computeIfAbsent(keyName, (keyName1) -> new HashSet<>())
                            .add(new PioIniItemBuilder(LookupElementBuilder
                                    .create(valueName)
                                    .withTypeText(keyName)
                                    .withIcon(icon)
                                    .bold(), IniTipFilters.getFilter(vplatform, vframework)));
                });
                eachByTagName(key, ARG, (arg) -> {
                    String valueName = arg.getTextContent().trim();
                    String vplatform = arg.getAttribute(PLATFORM);
                    String vframework = arg.getAttribute(FRAMEWORK);
                    values.computeIfAbsent(keyName, (keyName1) -> new HashSet<>())
                            .add(new PioIniItemBuilder(LookupElementBuilder
                                    .create(new Arg(valueName)).withInsertHandler(PlatformioIniMetaFactory::fixBuildFlag)
                                    .withTypeText(keyName)
                                    .withIcon(icon)
                                    .bold(), IniTipFilters.getFilter(vplatform, vframework)));
                });
            });
        });

        Element platformRule = getFirstElementByName(documentElement, PLATFORM_RULE);
        if (platformRule == null) {
            return;
        }
        platformRules.clear();
        eachChildrenElement(platformRule, platform ->{
            String platformName = platform.getTagName().trim();
            String value = platform.getAttribute(VALUE).trim();
            try {
                Pattern compile = Pattern.compile(value);
                platformRules.add(new PlatformRule(compile, platformName));
            }catch (Exception e) {
                NOTIFICATION_GROUP.createNotification(getMsg("notification.group.platformio-plus"),
                        getMsgF("platformio.ini.meta.load.failed", e.getMessage()), NotificationType.ERROR).notify(null);
            }
        });
        IniTipFilters.init();
    }

    /**
     * 补全空格等号
     */
    private static void fixKeyTipSuffix(@NotNull InsertionContext context, @NotNull LookupElement item) {
        int tailOffset = context.getTailOffset();
        Editor editor = context.getEditor();
        editor.getDocument().insertString(tailOffset, KEY_TIP_SUFFIX);
        editor.getCaretModel().moveToOffset(tailOffset + KEY_TIP_SKIP_LEN);
    }

    /**
     * 减号不会被自动补全识别，在已输入-D xxx情况下补全时忽略了前面的-会导致 补全后出现 -- 这里删掉重复的 -
     */
    private static void fixBuildFlag(@NotNull InsertionContext context, @NotNull LookupElement item) {
        int startOffset = context.getStartOffset();
        var document = context.getDocument();
        CharSequence charsSequence = document.getCharsSequence();
        if (startOffset > 1 && startOffset < charsSequence.length()
                && charsSequence.charAt(startOffset - 1) == ARG_SYMBOL
                && charsSequence.charAt(startOffset) == ARG_SYMBOL) {
            document.deleteString(startOffset, startOffset + 1);
        }
    }

    public static HashMap<String, Set<PioIniItemBuilder>> getKeys() {
        return INSTANCE.keys;
    }

    public static Set<LookupElementBuilder> getSections() {
        return INSTANCE.sections;
    }

    public static ArrayList<PlatformRule> getPlatformRules() {
        return INSTANCE.platformRules;
    }

    public static HashMap<String, Set<PioIniItemBuilder>> getValues() {
        return INSTANCE.values;
    }
}
